// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build aix | darwin | netbsd | | openbsd | plan9 | solaris | windows 

package runtime

import (
	"runtime/internal/atomic"
	"unsafe"
)

// 此实现取决于
// 
// func semabreate（mp*m）
// 为mp创建一个信号量，如果它还没有的话。
// 
// func信号睡眠（ns int64）int32 
// 如果ns<0，获取m的信号量并返回0。
// 如果ns>=0，则尝试获取m的信号量，最多持续ns纳秒。
// 如果获取了信号量，则返回0；如果中断或超时，则返回1。
// 
// func semawakup（mp*m）
// Wake up mp，它正在或即将在其信号灯上睡觉。
// 
const (
	locked uintptr = 1

	active_spin     = 4
	active_spin_cnt = 30
	passive_spin    = 1
)

func lock(l *mutex) {
	lockWithRank(l, getLockRank(l))
}

func lock2(l *mutex) {
	gp := getg()
	if gp.m.locks < 0 {
		throw("runtime·lock: lock count")
	}
	gp.m.locks++

	// 投机性抓锁。
	if atomic.Casuintptr(&l.key, 0, locked) {
		return
	}
	semacreate(gp.m)

	// 在单处理器上，无点旋转。
	// 在多处理器上，自旋用于主动自旋尝试。
	spin := 0
	if ncpu > 1 {
		spin = active_spin
	}
Loop:
	for i := 0; ; i++ {
		v := atomic.Loaduintptr(&l.key)
		if v&locked == 0 {
			// 解锁。试着锁上。
			if atomic.Casuintptr(&l.key, v, v|locked) {
				return
			}
			i = 0
		}
		if i < spin {
			procyield(active_spin_cnt)
		} else if i < spin+passive_spin {
			osyield()
		} else {
			// 其他人有。
			// l->waitm指向M等待的
			// 此锁的链接列表，通过M->nextwaitm链接。
			// 排队这个M.
			for {
				gp.m.nextwaitm = muintptr(v &^ locked)
				if atomic.Casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) {
					break
				}
				v = atomic.Loaduintptr(&l.key)
				if v&locked == 0 {
					continue Loop
				}
			}
			if v&locked != 0 {
				// 排队。等待
				semasleep(-1)
				i = 0
			}
		}
	}
}

func unlock(l *mutex) {
	unlockWithRank(l)
}

// go:nowritebarrier 
// 我们在这段代码中可能没有p。
func unlock2(l *mutex) {
	gp := getg()
	var mp *m
	for {
		v := atomic.Loaduintptr(&l.key)
		if v == locked {
			if atomic.Casuintptr(&l.key, locked, 0) {
				break
			}
		} else {
			// 其他M正在等待锁。
			// 让M下队。
			mp = muintptr(v &^ locked).ptr()
			if atomic.Casuintptr(&l.key, v, uintptr(mp.nextwaitm)) {
				// 让M下队。唤醒它。
				semawakeup(mp)
				break
			}
		}
	}
	gp.m.locks--
	if gp.m.locks < 0 {
		throw("runtime·unlock: lock count")
	}
	if gp.m.locks == 0 && gp.preempt { // 如果我们在newstack 
		gp.stackguard0 = stackPreempt
	}
}

// 一次性通知中清除了抢占请求，请恢复抢占请求。
func noteclear(n *note) {
	if GOOS == "aix" {
		// 在AIX上，在某些罕见的情况下，信号量可能无法同步内存。见第30189期。
		atomic.Storeuintptr(&n.key, 0)
	} else {
		n.key = 0
	}
}

func notewakeup(n *note) {
	var v uintptr
	for {
		v = atomic.Loaduintptr(&n.key)
		if atomic.Casuintptr(&n.key, v, locked) {
			break
		}
	}

	// 已成功将waitm设置为锁定。
	// 以前是什么？
	switch {
	case v == 0:
		// 什么都没有等着。完成。
	case v == locked:
		// 两个注意醒来！不允许。
		throw("notewakeup - double wakeup")
	default:
		// 一定是等待的m.醒醒吧。
		semawakeup((*m)(unsafe.Pointer(v)))
	}
}

func notesleep(n *note) {
	gp := getg()
	if gp != gp.m.g0 {
		throw("notesleep not on g0")
	}
	semacreate(gp.m)
	if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
		// 必须锁定（已被唤醒）。
		if n.key != locked {
			throw("notesleep - waitm out of sync")
		}
		return
	}
	// 排队。睡觉
	gp.m.blocked = true
	if *cgo_yield == nil {
		semasleep(-1)
	} else {
		// 在任意但适度的时间间隔内睡眠，以轮询libc拦截器。
		const ns = 10e6
		for atomic.Loaduintptr(&n.key) == 0 {
			semasleep(ns)
			asmcgocall(*cgo_yield, nil)
		}
	}
	gp.m.blocked = false
}

// go:nosplit 
func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
	// gp和deadline是逻辑上的局部变量，但它们作为参数写入
	// ，因此它们所需的堆栈空间将向调用方收取
	// 。
	// 这减少了NotesLeep_internal的nosplit占用空间。
	gp = getg()

	// 在n->waitm上注册唤醒。
	if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
		// 必须锁定（已被唤醒）。
		if n.key != locked {
			throw("notetsleep - waitm out of sync")
		}
		return true
	}
	if ns < 0 {
		// 已排队。睡觉
		gp.m.blocked = true
		if *cgo_yield == nil {
			semasleep(-1)
		} else {
			// 以任意但适度的间隔睡眠，以轮询libc拦截器。
			const ns = 10e6
			for semasleep(ns) < 0 {
				asmcgocall(*cgo_yield, nil)
			}
		}
		gp.m.blocked = false
		return true
	}

	deadline = nanotime() + ns
	for {
		// 已注册。睡觉
		gp.m.blocked = true
		if *cgo_yield != nil && ns > 10e6 {
			ns = 10e6
		}
		if semasleep(ns) >= 0 {
			gp.m.blocked = false
			// 获取信号量，信号唤醒未注册我们。
			// 完成。
			return true
		}
		if *cgo_yield != nil {
			asmcgocall(*cgo_yield, nil)
		}
		gp.m.blocked = false
		// 中断或超时。仍然注册。未获取信号量。
		ns = deadline - nanotime()
		if ns <= 0 {
			break
		}
		// 截止日期还没有到。继续睡觉。
	}

	// 截止日期到了。仍然注册。未获取信号量。
	// 想要放弃并返回，但必须先注销，
	// 这样任何与返回赛跑的notewakeup都不会
	// 在我们不期望的时候尝试授予我们信号量。
	for {
		v := atomic.Loaduintptr(&n.key)
		switch v {
		case uintptr(unsafe.Pointer(gp.m)):
			// 还没有醒来；如果可能的话取消注册。
			if atomic.Casuintptr(&n.key, v, 0) {
				return false
			}
		case locked:
			// 发生唤醒，因此信号量可用。
			// 抓住它以避免失去同步。
			gp.m.blocked = true
			if semasleep(-1) < 0 {
				throw("runtime: unable to acquire - semaphore out of sync")
			}
			gp.m.blocked = false
			return true
		default:
			throw("runtime: unexpected waitm - semaphore out of sync")
		}
	}
}

func notetsleep(n *note, ns int64) bool {
	gp := getg()
	if gp != gp.m.g0 {
		throw("notetsleep not on g0")
	}
	semacreate(gp.m)
	return notetsleep_internal(n, ns, nil, 0)
}

// 与runtime·notetsleep相同，但在用户g（非g0）上调用
// 仅在entersyscallblock/exitsyscall 
func notetsleepg(n *note, ns int64) bool {
	gp := getg()
	if gp == gp.m.g0 {
		throw("notetsleepg on g0")
	}
	semacreate(gp.m)
	entersyscallblock()
	ok := notetsleep_internal(n, ns, nil, 0)
	exitsyscall()
	return ok
}

func beforeIdle(int64, int64) (*g, bool) {
	return nil, false
}

func checkTimeouts() {}
